﻿/* * * * * * * * * * * * * * * *
 * SDL ToolBox                 *
 * Copyright (c) VosSoft 2009  *
 * * * * * * * * * * * * * * * *
 * file:    class map          *
 * date:    2009-05-05         *
 * * * * * * * * * * * * * * * */

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace SDL_ToolBox
{
    public class Map
    {
        /// <summary>
        /// Speicherort der Map
        /// </summary>
        private string name;
        /// <summary>
        /// Breite der Map (in Blöcken)
        /// </summary>
        private int x;
        /// <summary>
        /// Höhe der Map (in Blöcken)
        /// </summary>
        private int y;
        /// <summary>
        /// Blockgröße der Map (in Pixel, nur intern))
        /// </summary>
        private int size;
        /// <summary>
        /// Hintergrundbild der Map (max 10 Zeichen)
        /// </summary>
        private char[] background;
        /// <summary>
        /// Vordergrundbild der Map (max 10 Zeichen)
        /// </summary>
        private char[] foreground;
        /// <summary>
        /// Layer 1 (unten), 2D-Texturenarray [x, y]
        /// </summary>
        private Texture[,] layer1;
        /// <summary>
        /// Layer 2 (oben), 2D-Texturenarray [x, y]
        /// </summary>
        private Texture[,] layer2;

        /// <summary>
        /// Standardkonstruktor
        /// </summary>
        public Map()
        {
            name = null;
            x = 0;
            y = 0;
            size = 0;
            background = new char[10];
            foreground = new char[10];
            layer1 = null;
            layer2 = null;
        }

        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="filename">Speicherort der Map</param>
        public Map(string filename)
        {
            name = filename;
            x = 0;
            y = 0;
            size = 0;
            background = new char[10];
            foreground = new char[10];
            layer1 = null;
            layer2 = null;
        }

        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="filename">Speicherort der Map</param>
        /// <param name="width">Breite der Map (in Blöcken)</param>
        /// <param name="height">Höhe der Map (in Blöcken)</param>
        /// <param name="blocksize">Blockgröße der Map (in Pixel)</param>
        public Map(string filename, int width, int height, int blocksize)
        {
            name = filename;
            x = width;
            y = height;
            size = blocksize;
            background = new char[10];
            foreground = new char[10];
            layer1 = Texture.NewArray(x, y);
            layer2 = Texture.NewArray(x, y);
        }

        /// <summary>
        /// Map mit einer Hintergrundtextur initialisieren
        /// </summary>
        /// <param name="filename">Speicherort der Map</param>
        /// <param name="width">Breite der Map (in Blöcken)</param>
        /// <param name="height">Höhe der Map (in Blöcken)</param>
        /// <param name="blocksize">Blockgröße der Map (in Pixel)</param>
        /// <param name="back">Hintergrundbild</param>
        /// <param name="fore">Vordergrundbild</param>
        /// <param name="tex">Hintergrundtextur</param>
        public Map(string filename, int width, int height, int blocksize, char[] back, char[] fore, Texture tex)
        {
            name = filename;
            x = width;
            y = height;
            size = blocksize;
            background = new char[10];
            back.CopyTo(background, 0);
            ToolBox.zeroToSpace(background);
            foreground = new char[10];
            fore.CopyTo(foreground, 0);
            ToolBox.zeroToSpace(foreground);
            layer1 = Texture.NewArray(x, y);
            layer2 = Texture.NewArray(x, y);
            for (int j = 0; j < y; j++)
            {
                for (int i = 0; i < x; i++)
                {
                    layer1[i, j].Copy(tex, false);
                    layer2[i, j].Copy(tex, false);
                }
            }
        }

        /// <summary>
        /// Speicherort der Map
        /// </summary>
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        /// <summary>
        /// Breite der Map (in Blöcken)
        /// </summary>
        public int X
        {
            get { return x; }
            //set { x = value; }
        }

        /// <summary>
        /// Höhe der Map (in Blöcken)
        /// </summary>
        public int Y
        {
            get { return y; }
            //set { y = value; }
        }

        /// <summary>
        /// Blockgröße der Map (in Pixel, nur intern)
        /// </summary>
        public int Size
        {
            get { return size; }
            //set { size = value; }
        }

        /// <summary>
        /// Hintergrundbild der Map (max 10 Zeichen)
        /// </summary>
        public char[] Background
        {
            get { return background; }
            set { background = value; }
        }

        /// <summary>
        /// Vordergrundbild der Map (max 10 Zeichen)
        /// </summary>
        public char[] Foreground
        {
            get { return foreground; }
            set { foreground = value; }
        }

        /// <summary>
        /// Layer 1 (unten), 2D-Texturenarray [x, y] der Map
        /// </summary>
        internal Texture[,] Layer1
        {
            get { return layer1; }
            set { layer1 = value; }
        }

        /// <summary>
        /// Layer 2 (oben), 2D-Texturenarray [x, y] der Map
        /// </summary>
        internal Texture[,] Layer2
        {
            get { return layer2; }
            set { layer2 = value; }
        }

        /// <summary>
        /// Map speichern
        /// </summary>
        /// <returns>Gibt true zurück, wenn das Speichern erfolgreich war</returns>
        public bool Write()
        {
            FileStream file = new FileStream(name, FileMode.OpenOrCreate, FileAccess.Write);
            if (file.CanWrite == true)
            {
                BinaryWriter bw = new BinaryWriter(file, Encoding.UTF8);
                // Header
                bw.Write(x);
                bw.Write(y);
                ToolBox.spaceToZero(background);
                bw.Write(background);
                ToolBox.spaceToZero(foreground);
                bw.Write(foreground);
                // Layer 1
                for (int j = 0; j < y; j++)
                {
                    for (int i = 0; i < x; i++)
                    {
                        bw.Write(layer1[i, j].X);
                        bw.Write(layer1[i, j].Y);
                        ToolBox.spaceToZero(layer1[i, j].Name);
                        bw.Write(layer1[i, j].Name);
                        bw.Write(layer1[i, j].OffX);
                        bw.Write(layer1[i, j].OffY);
                        bw.Write(layer1[i, j].Size);
                        bw.Write(layer1[i, j].R);
                        bw.Write(layer1[i, j].G);
                        bw.Write(layer1[i, j].B);
                    }
                }
                // Layer 2
                for (int j = 0; j < y; j++)
                {
                    for (int i = 0; i < x; i++)
                    {
                        bw.Write(layer2[i, j].X);
                        bw.Write(layer2[i, j].Y);
                        ToolBox.spaceToZero(layer2[i, j].Name);
                        bw.Write(layer2[i, j].Name);
                        bw.Write(layer2[i, j].OffX);
                        bw.Write(layer2[i, j].OffY);
                        bw.Write(layer2[i, j].Size);
                        bw.Write(layer2[i, j].R);
                        bw.Write(layer2[i, j].G);
                        bw.Write(layer2[i, j].B);
                    }
                }
                bw.Close();
                file.Close();
                return true;
            }
            else
            {
                file.Close();
                return false;
            }
        }

        /// <summary>
        /// Map speichern
        /// </summary>
        /// <param name="filename">Speicherort der Map</param>
        /// <returns>Gibt true zurück, wenn das Speichern erfolgreich war</returns>
        public bool Write(string filename)
        {
            name = filename;
            return Write();
        }

        /// <summary>
        /// Map einlesen
        /// </summary>
        /// <param name="tex">Texturen, die in der Map verwendet werden</param>
        /// <returns>Gibt true zurück, wenn das Öffnen erfolgreich war</returns>
        public bool Read(Dictionary<string, Map> tex)
        {
            if (File.Exists(name) == true)
            {
                FileStream file = new FileStream(name, FileMode.Open, FileAccess.Read);
                if (file.CanRead == true)
                {
                    BinaryReader br = new BinaryReader(file, Encoding.UTF8);
                    // Header
                    x = br.ReadInt32();
                    y = br.ReadInt32();
                    background = br.ReadChars(background.Length);
                    ToolBox.zeroToSpace(background);
                    foreground = br.ReadChars(foreground.Length);
                    ToolBox.zeroToSpace(foreground);
                    // Layer 1
                    layer1 = Texture.NewArray(x, y);
                    for (int j = 0; j < y; j++)
                    {
                        for (int i = 0; i < x; i++)
                        {
                            try
                            {
                                br.ReadInt32(); // Texture.X ignorieren
                                br.ReadInt32(); // Texture.Y ignorieren
                                layer1[i, j].Name = br.ReadChars(layer1[i, j].Name.Length);
                                ToolBox.zeroToSpace(layer1[i, j].Name);
                                layer1[i, j].OffX = br.ReadInt32();
                                layer1[i, j].OffY = br.ReadInt32();
                                layer1[i, j].Size = br.ReadInt32();
                                layer1[i, j].R = br.ReadUInt32();
                                layer1[i, j].G = br.ReadUInt32();
                                layer1[i, j].B = br.ReadUInt32();
                                layer1[i, j].Bmp = tex[new String(layer1[i, j].Name).TrimEnd()].layer1[layer1[i, j].OffX, layer1[i, j].OffY].Bmp;
                            }
                            catch (KeyNotFoundException)
                            {
                                throw new KeyNotFoundException("Die Textur \"" + new String(layer1[i, j].Name).TrimEnd() + "\" wurde nicht gefunden!\nDie Texturen müssen vor dem Laden der Map geladen sein!");
                            }
                        }
                    }
                    // Layer 2
                    layer2 = Texture.NewArray(x, y);
                    for (int j = 0; j < y; j++)
                    {
                        for (int i = 0; i < x; i++)
                        {
                            try
                            {
                                br.ReadInt32(); // Texture.X ignorieren
                                br.ReadInt32(); // Texture.Y ignorieren
                                layer2[i, j].Name = br.ReadChars(layer2[i, j].Name.Length);
                                ToolBox.zeroToSpace(layer2[i, j].Name);
                                layer2[i, j].OffX = br.ReadInt32();
                                layer2[i, j].OffY = br.ReadInt32();
                                layer2[i, j].Size = br.ReadInt32();
                                layer2[i, j].R = br.ReadUInt32();
                                layer2[i, j].G = br.ReadUInt32();
                                layer2[i, j].B = br.ReadUInt32();
                                layer2[i, j].Bmp = tex[new String(layer2[i, j].Name).TrimEnd()].layer1[layer2[i, j].OffX, layer2[i, j].OffY].Bmp;
                            }
                            catch (KeyNotFoundException)
                            {
                                throw new KeyNotFoundException("Die Textur \"" + new String(layer2[i, j].Name).TrimEnd() + "\" wurde nicht gefunden!\nDie Texturen müssen vor dem Laden der Map geladen sein!");
                            }
                        }
                    }
                    size = layer1[0, 0].Size;
                    br.Close();
                    file.Close();
                    return true;
                }
                else
                {
                    file.Close();
                    return false;
                }
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Map einlesen
        /// </summary>
        /// <param name="filename">Speicherort der Map</param>
        /// <param name="tex">Texturen, die in der Map verwendet werden</param>
        /// <returns>Gibt true zurück, wenn das Öffnen erfolgreich war</returns>
        public bool Read(string filename, Dictionary<string, Map> tex)
        {
            name = filename;
            return Read(tex);
        }

        /// <summary>
        /// Läd anhand eines Bildes ein Texturenarray
        /// </summary>
        /// <param name="img">Referenz auf die Textur</param>
        /// <param name="size">Blockgröße der Textur</param>
        /// <param name="progress">Progress-Steuerelement um den Ladestatus anzuzeigen</param>
        public void LoadTexturesFromImage(Image img, int size, ToolStripProgressBar progress)
        {
            progress.Maximum = y;
            progress.Step = 1;
            progress.Value = progress.Minimum;
            for (int j = 0; j < img.Height / size; j++)
            {
                for (int i = 0; i < img.Width / size; i++)
                {
                    layer1[i, j].Set(Path.GetFileName(name).ToCharArray(), i, j, size);
                    // Bildausschnitt extrahieren
                    layer1[i, j].Bmp = ToolBox.CropImage(img, i * size, j * size, size, size);
                }
                progress.PerformStep();
            }
        }

        /// <summary>
        /// Erstellt ein Vorschaubild der Map anhand der Texturen
        /// </summary>
        /// <param name="layer">Texturlayer (1 oder 2)</param>
        /// <returns>Bild der Map</returns>
        public Bitmap GetImage(int layer)
        {
            Bitmap bmp = new Bitmap(x * size, y * size);
            Graphics graph = Graphics.FromImage(bmp);
            for (int j = 0; j < y; j ++)
            {
                for (int i = 0; i < x; i ++)
                {
                    if (layer == 2)
                    {
                        graph.DrawImage(layer2[i, j].Bmp, i * layer2[i, j].Size, j * layer2[i, j].Size, layer2[i, j].Size, layer2[i, j].Size);
                    }
                    else // Layer 1
                    {
                        graph.DrawImage(layer1[i, j].Bmp, i * layer1[i, j].Size, j * layer1[i, j].Size, layer1[i, j].Size, layer1[i, j].Size);
                    }
                }
            }
            graph.Dispose();
            return bmp;
        }
    }
}
